﻿using IronPython.Hosting;
using IronPython.Runtime.Exceptions;
using Microsoft.Scripting.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Windows.Forms;
using System.Xml.Linq;

namespace NFox.Pycad
{

    /// <summary>
    /// Pycad引擎
    /// </summary>
    public class Engine
    {

        #region Paths

        public static DirectoryInfo MainPath { get; set; }

        /// <summary>
        /// 当前目录
        /// </summary>
        public static DirectoryInfo Path { get; set; }

        /// <summary>
        /// IronPython目录
        /// </summary>
        public static FileInfo PythonLibPath { get; set; }

        /// <summary>
        /// 支持文件目录
        /// </summary>
        public static DirectoryInfo SupportPath { get; set; }


        public static DirectoryInfo ProjectPath { get; set; }

        public static DirectoryInfo TempPath { get; set; }


        public static DirectoryInfo UpdatePath { get; set; }

        public static void SetPaths()
        {
            Path = DirectoryEx.CurrDirectory;
            MainPath = Path.Parent;
            SupportPath = MainPath.GetDirectory("Support");
            PythonLibPath = SupportPath.GetFile("Lib.zip");
            ProjectPath = MainPath.GetDirectory("Projects");
            TempPath = MainPath.GetDirectory("Temp");
            UpdatePath = MainPath.GetDirectory("Update");
        }

        #endregion

        #region Projects

        public Project Projects { get; set; }

        public void GetProjects()
        {
            Projects = new Project("所有项目", ProjectPath);
        }

        public void LoadProjects()
        {
            foreach (Project p in Projects.Packages)
            {
                if (p.Type == Project.ProjectType.Compiled)
                {
                    if (File.Exists(p.Path))
                    {
                        var code = $"clr.AddReferenceToFileAndPath('{p.Path}')";
                        Execute(code.Replace("\\", "/"));
                    }
                }
                Import(p.Name);
                LoadSubPackage(p, "loading");
            }
        }

        public void RegistCommands()
        {
            foreach (Project p in Projects.Packages)
            {
                LoadSubPackage(p, "commands");
            }
        }

        private void LoadSubPackage(Project prj, string name)
        {
            var package = prj.GetPackage(name);
            if (package != null)
            {
                Execute($"try: import {package.FullName}\nexcept: pass");
                foreach(var m in package.Modules)
                    Execute($"try: import {m.FullName}\nexcept: pass");
                foreach (var p in package.Packages)
                    Execute($"try: import {p.FullName}\nexcept: pass");
            }
        }

        public void AddSysProject(DirectoryInfo dir)
        {
            if (dir != null && Directory.Exists(dir.FullName))
            {
                if(Projects.GetPackage(dir.Name) == null)
                {
                    Projects.Add(new Project(dir) { Visible = false, IsSystem = true });
                }

            }
        }

        private bool _showsysprjs;
        public bool ShowSysProjects()
        {
            _showsysprjs = !_showsysprjs;
            foreach (var prj in Projects.Packages)
            {
                if(prj.IsSystem)
                {
                    prj.Visible = _showsysprjs;
                }
            }
            return _showsysprjs;
        }

        #endregion

        ScriptEngine _engine;
        ScriptScope _scope;

        public ScriptRuntime Runtime
        {
            get { return _engine.Runtime; }
        }

        private void Load()
        {
            try
            {

                //生成py引擎
                var options = new Dictionary<string, object>();
                options["Frames"] = true;
                options["FullFrames"] = true;
                _engine = Python.CreateEngine(AppDomain.CurrentDomain, options);

                //设置搜索目录
                var paths = _engine.GetSearchPaths();
                paths.Add(Path.FullName);
                paths.Add(PythonLibPath.FullName);
                paths.Add(SupportPath.FullName);
                paths.Add(ProjectPath.FullName);
                _engine.SetSearchPaths(paths);
                _scope = _engine.CreateScope();
                _loaded = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Err:" + ex.Message);
            }

        }

        private bool _loaded;
        private void Unload()
        {
            foreach (Project p in Projects.Packages)
                LoadSubPackage(p, "unloaded"); 
        }

        public void Restart()
        {
            if (_loaded) Unload();
            Load();
        }

        public dynamic GetImportClass(string name)
        {
            Import(name);
            return Execute(name);
        }

        public void Import(string name)
        {
            Execute($"import {name}");
        }

        public void AddSearchPath(string path)
        {
            var paths = _engine.GetSearchPaths();
            paths.Add(path);
            _engine.SetSearchPaths(paths);
        }

        public void LoadAssembly(Assembly assem)
        {
            _engine.Runtime.LoadAssembly(assem);
        }

        public dynamic GetVariable(string name)
        {
            return _scope.GetVariable(name);
        }

        public dynamic SetVariable(string name, dynamic value)
        {
            return _scope.SetVariable(name, value);
        }

        public void Print(object res)
        {
            Execute("pye.settempvalue")(res);
            Execute("print pye.gettempvalue()");
        }

        public dynamic Execute(string code)
        {
            ScriptSource script =
               _engine.CreateScriptSourceFromString(code);
            return script.Execute(_scope);
        }

        public dynamic Execute(string code, bool showerr)
        {
            try
            {
                ScriptSource script =
                   _engine.CreateScriptSourceFromString(code);
                return script.Execute(_scope);
            }
            catch (Exception ex)
            {
                if (showerr) MessageBox.Show(ex.Message);
                return null;
            }
        }

        public void SetTrace(TracebackDelegate func)
        {
            _engine.SetTrace(func);
        }

        public dynamic Execute(string code, ScriptScope scope)
        {
            ScriptSource script =
               _engine.CreateScriptSourceFromString(code);
            return script.Execute(scope);
        }

        public void TryReference(string dllname)
        {
            Execute($"import clr;clr.AddReference('{dllname}')", false);
        }

        public dynamic CreateScope()
        {
            return _engine.CreateScope();
        }

        public dynamic ExecuteFile(string path)
        {
            ScriptSource script =
               _engine.CreateScriptSourceFromFile(path);
            return script.Execute(_scope);
        }

        public dynamic ExecuteFile(string path, ScriptScope scope)
        {
            ScriptSource script =
               _engine.CreateScriptSourceFromFile(path);
            return script.Execute(scope);
        }

        public void Release(string name, List<Module> modules)
        {
            string path = $"{TempPath.FullName}\\Release.zip";
            using (FileStream target = new FileStream(path, FileMode.Create))
            {
                using (ZipArchive zip = new ZipArchive(target, ZipArchiveMode.Update))
                {
                    XElement xe = XElement.Load(SupportPath.GetFile("Package.xml").FullName);
                    CopyFiles(xe, MainPath, zip);
                    foreach (var m in modules)
                    {
                        using (var stream = zip.CreateEntry($"Update\\Projects\\{m.Name}").Open())
                            Build(m.Name, stream);
                    }
                }
            }
            DynamicCompiler.BuildReleaseAssembly($"{TempPath.FullName}\\{name}.Setup.dll", path);
        }

        private void CopyFiles(XElement xe, DirectoryInfo dir, ZipArchive zip)
        {
            foreach (var e in xe.Elements())
            {
                string name = e.Attribute("Name").Value;
                if (e.Name == "Directory")
                {
                    CopyFiles(e, dir.GetDirectory(name), zip);
                }
                else
                {
                    if (e.Attribute("Build") != null && bool.Parse(e.Attribute("Build").Value))
                    {
                        using (var stream = zip.CreateEntry($"Update\\{e.Attribute("Name").Value}").Open())
                            Build(name, stream);
                    }
                    else
                    {
                        using (var stream = zip.CreateEntry($"Update\\{name}").Open())
                        {
                            var file = dir.GetFile(name);
                            using (FileStream source = file.OpenRead())
                                source.CopyTo(stream);
                        }
                    }
                }
            }
        }

        public void Build(string name, Stream stream)
        {

            var project = Projects.GetProject(name);
            if (project == null)
                project = new Project(SupportPath.GetDirectory(name));

            using (ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Update))
            {
                string targetpath;
                if (project.DataDirectory != null)
                {
                    foreach (var file in project.DataDirectory.GetFiles())
                    {
                        using (var target = zip.CreateEntry($"{name}.data\\{file.Name}").Open())
                        {
                            using (var source = file.OpenRead())
                                source.CopyTo(target);
                        }
                    }
                }

                targetpath = $"{TempPath.FullName}\\{name}.dll";
                List<string> names = new List<string>();
                project.GetFileNames(names);
                var code = $"clr.CompileModules('{targetpath}',{string.Join(",", names)});";
                Execute(code.Replace("\\", "/"));
                var dllfile = new FileInfo(targetpath);
                using (var target = zip.CreateEntry($"{name}.dll").Open())
                {
                    using (var source = dllfile.OpenRead())
                        source.CopyTo(target);
                }
                dllfile.Delete();
            }

        }

        public void Setup()
        {
            using (var dlg = new OpenFileDialog())
            {
                dlg.Title = "选择.NET程序集";
                dlg.InitialDirectory = "c:\\";
                dlg.Filter = "项目文件(*.prj)|*.prj|安装文件(*.dll)|*.dll";
                dlg.RestoreDirectory = true;
                dlg.FilterIndex = 0;
                
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    

                }
            }
        }


    }



}
